// 生成和验证 JWT 令牌的工具，用户登录后用令牌验证身份

// 导入工具：jsonwebtoken（生成/验证JWT）、dotenv（加载环境变量）
const jwt = require("jsonwebtoken");
require("dotenv").config();

// 校验环境变量是否存在（启动时执行一次，提前暴露配置问题）
// 避免每次生成/验证令牌时都重复检查
let envChecked = false; //默认未检查
const checkEnvVariables = () => {
  // 已检查过则跳过
  if (envChecked) {
    return;
  }
  // 检查JWT密钥是否配置
  if (!process.env.JWT_SECRET) {
    throw new Error("环境变量 JWT_SECRET 未配置，请在 .env 中添加");
  }
  // 检查过期时间是否配置
  if (!process.env.JWT_EXPIRES_IN) {
    throw new Error("环境变量 JWT_EXPIRES_IN 未配置，请在 .env 中添加");
  }
  // 简单校验过期时间格式（jwt支持：数字+单位，如 1h、7d、3600s）
  const expiresInRegex = /^\d+[smhd]$/; // 匹配 数字+单位（s秒/m分/h时/d天）
  if (!expiresInRegex.test(process.env.JWT_EXPIRES_IN)) {
    throw new Error("JWT_EXPIRES_IN 格式错误，示例：1h、7d、3600s");
  }
  // 标记已检查，避免重复检查
  envChecked = true;
};

// 启动时执行环境变量校验（提前暴露问题，避免运行时错误）
checkEnvVariables();

/**
 * 生成JWT令牌
 * @param {number|string} userId - 用户ID（用于后续从令牌中识别用户）
 * @returns {string} 生成的JWT令牌
 */
const generateToken = (userId) => {
  // 校验userId是否有效（避免传入空值或非字符串/数字）
  if (!userId || (typeof userId !== "number" && typeof userId !== "string")) {
    throw new Error("生成令牌失败：userId必须为非空字符串或数字");
  }

  // 生成令牌：
  // payload：存储用户ID（仅必要信息，避免敏感数据）
  // secret：加密密钥（从环境变量取，必须保密）
  // options：配置过期时间
  return jwt.sign({ id: userId }, process.env.JWT_SECRET, {
    // 令牌过期时间（从.env取）
    expiresIn: process.env.JWT_EXPIRES_IN,
  });
};

/**
 * 验证JWT令牌
 * @param {string} token - 待验证的JWT令牌
 * @returns {object} 令牌解析后的payload（包含用户ID等信息）
 * @throws {Error} 验证失败时抛出具体错误（如令牌过期、签名无效等）
 */
const verifyToken = (token) => {
  // 校验令牌是否为空
  if (!token || typeof token !== "string") {
    throw new Error("验证失败：令牌为空或格式错误");
  }

  try {
    // 验证令牌并返回解析后的payload
    // 若验证失败（如过期、签名错误），jwt.verify会自动抛出对应错误
    return jwt.verify(token, process.env.JWT_SECRET);
  } catch (error) {
    // 捕获并转换错误信息，让错误提示更友好
    if (error.name === "TokenExpiredError") {
      throw new Error("令牌已过期，请重新登录");
    } else if (error.name === "JsonWebTokenError") {
      // JsonWebTokenError包含多种情况：签名错误、令牌格式错误等
      throw new Error("令牌无效，签名错误或格式不正确");
    } else {
      // 其他未知错误（如密钥错误）
      throw new Error("令牌验证失败：" + error.message);
    }
  }
};

// 导出工具函数（供登录控制器、认证中间件使用）
module.exports = {
  generateToken,
  verifyToken,
};
